# Rendering using our own shader

Now that we can enjoy our sphere from all angles, it is time to customize the way it is rendered. This means writing our own shader to alter the rendering directly on GPU.

As we are using a DirectX renderer, we will write it in HLSL. If you are unfamiliar with this language, it is advised to check some documentation about it to fully understand what we will be doing here. For instance, you can find some good knowledge here or there.

## Creating a Program

Within nkGraphics, the class symbolizing a shader program is called Program. Let's start manipulating it, by first including :

#include <NilkinsGraphics/Programs/Program.h> #include <NilkinsGraphics/Programs/ProgramManager.h> #include <NilkinsGraphics/Programs/ProgramSourcesHolder.h>

Having those includes will allow to alter the code to use a Program. Let's put this code right after we create the entity and populate it with the mesh :

nkGraphics::Program* program = nkGraphics::ProgramManager::getInstance()->createOrRetrieve("program") ;

A Program is a resource like any other, and as such we use its dedicated manager to get one. Our local program will be named "program"... How original.

// We will feed it from memory, prepare the holder and fit the HLSL sources we will need nkGraphics::ProgramSourcesHolder sources ; // Simple vertex stage positioning the mesh sources.setVertexMemory ( R"eos( cbuffer PassBuffer : register(b0) { matrix view ; matrix proj ; } struct VertexInput { float4 position : POSITION ; matrix world : WORLD_MATRIX ; } ; struct PixelInput { float4 position : SV_POSITION ; } ; PixelInput main (VertexInput input) { PixelInput result ; matrix mvp = mul(proj, mul(view, input.world)) ; result.position = mul(mvp, input.position) ; return result ; } )eos" ) ; // Pixel stage will return a constant color sources.setPixelMemory ( R"eos( struct PixelInput { float4 position : SV_POSITION ; } ; float4 main (PixelInput input) : SV_TARGET { return float4(0.8, 0.4, 0.2, 1.0) ; } )eos" ) ; // Program can then be fed by the sources we created, and loaded // Load operation will trigger its source compilation and linking program->setFromMemory(sources) ; program->load() ;

The most important part of a Program are the sources. They go into a ProgramSourcesHolder structure which can be submitted to the Program for compilation.

As we do a simple shader supposed to paint a mesh, we will need the vertex and pixel stages.
We won't go over the details of the HLSL code in this tutorial, as its only aim is to transform the mesh onto the screen and apply a constant colour.
However, an important point to notice is the fact that the entry point methods should be called main. Those are used by the program compilation within the component.

Back to C++, we specify the program should load from the memory we just populated, and request a load operation, as usual with resources in the component.

Now, notice the vertex stage input :

- A constant buffer, with the view and projection matrices
- The vertex data, being the position of the vertex, and an attached world matrix, available with semantic names POSITION and WORLD_MATRIX

Part of this input is coming from the mesh that will get drawn using the shader program. As mentioned in tutorial N°03, it is important to have the right semantic names for our attributes, found in the mesh layouts.
First, POSITION is expected from the mesh, and implies that there is an attribute named like this in the mesh layout.
Last attribute has the name WORLD_MATRIX, and is tied to the Shader's instance slots we will discover later. They also link their data to a semantic name it is important to refer to.
If the combination of a Mesh and a Shader fails in feeding what a Program requires, it won't be possible to link them together.

However, as mentioned just now, we need a way to provide input a mesh won't cover to the program we just created. This is where we properly introduce the Shader class.

## Controlling the input through a Shader

A Shader represents an association between a Program and an input. It is possible to have one Program linked to many Shaders, each with different input to feed it.

Now that we know the next step we need, let's see the required includes :

#include <NilkinsGraphics/Shaders/Memory/ConstantBuffer.h> #include <NilkinsGraphics/Shaders/Memory/ShaderInstanceMemorySlot.h> #include <NilkinsGraphics/Shaders/Memory/ShaderPassMemorySlot.h> #include <NilkinsGraphics/Shaders/Shader.h> #include <NilkinsGraphics/Shaders/ShaderManager.h>

And use them right away :

nkGraphics::Shader* shader = nkGraphics::ShaderManager::getInstance()->createOrRetrieve("shader") ;

Our shader is now created. Let's see what we can do with it :

// First, attach its program we compiled before shader->setProgram(program) ; // In HLSL, we declared a constant buffer, which will be auto linked to slot 0 // We do the same here, on slot 0 nkGraphics::ConstantBuffer* cBuffer = shader->addConstantBuffer(0) ; // Within nkGraphics, way to feed data is by using slots // We need to give matrices to our HLSL, we declare them in the same order as they will be fed sequentially nkGraphics::ShaderPassMemorySlot* slot = cBuffer->addPassMemorySlot() ; slot->setAsViewMatrix() ; slot = cBuffer->addPassMemorySlot() ; slot->setAsProjectionMatrix() ; // Last bit is the instance buffer // This slot will be fed by instance the shader has to render // We need the world matrix, which corresponds to the final graph node matrix to place the mesh in the world nkGraphics::ShaderInstanceMemorySlot* instanceSlot = shader->addInstanceMemorySlot() ; instanceSlot->setAsWorldMatrix() ; // Eventually load the shader to enable it to prepare all it needs shader->load() ;

We start by attaching its program right away; the one we just created. Next, we register a ConstantBuffer inside, which is precisely what the Program needs.

The ConstantBuffer is formed by ShaderPassMemorySlots. Basically, a pass slot is fed once per pass, right before rendering it. Slots offer many capabilities, and amongst them is feeding a constant buffer with the view and projection matrices.
This relates directly to the constant buffer given within the program, so the order of declaration is important. First comes the view matrix, then comes the projection matrix, like we declared them in HLSL.

Next is the creation of a ShaderInstanceMemorySlot. This type of slot is specific to the rendering of a render queue. Basically, it is triggered once per instance of mesh that needs to be rendered. This type of slot maps directly to an attribute within the mesh, and is tied to it through its semantic name. By setting it as a world matrix, we will get a matrix with semantic name WORLD_MATRIX, representing the transformation matrix for a given instance of a mesh. This transformation comes from the Node attached to the Entity containing the mesh.

To summarize, within HLSL, we have the vertex position, and the world matrix. The vertex position is given by the mesh itself. However, the world matrix depends on the Entity, and the Node it is attached to, changing its position, orientation, or scale within the virtual world.
This specific data is given through the slot meant for instances. By specifying we want it to feed the world matrix, we ensure it will do so for each instance in our rendering.

Final step is to load the shader, like we always do with resources we use. Now the shader is ready to be used during rendering. To do so, we can now replace the nullptr shader argument provided to the Entity :

ent->setRenderInfo(nkGraphics::EntityRenderInfo(mesh, shader)) ;

Let's see right away what we will get when launching the program !

The sphere turned red !

This covers the way to create and use a shader within the component. For now, it is pretty simple, but in the next steps we will see how to spice it a bit.